#include "thread.h"
#include "log.h"
#include "util.h"

namespace framework
{
static framework::Logger::ptr g_logger = FRAMEWORK_LOG_NAME("system");


//thread_local关键字修饰的变量具有线程（thread）周期,这些变量在线程开始的时候被生成，
//在线程结束的时候被销毁，并且每一个线程都拥有一个独立的变量实例。
static thread_local Thread* t_thread = nullptr;
static thread_local std::string t_thread_name = "UNKNOW";


//获取当前的线程指针
Thread* Thread::GetThis()
{
    return t_thread;
}
//获取当前线程名称
const std::string& Thread::GetName()
{
    return t_thread_name;
}
//设置当前线程名称
void Thread::SetName(const std::string& name)
{
    if(name.empty())
    {
        return;
    }
    if(t_thread)
    {
        t_thread->m_name = name;
    }
    t_thread_name = name;
}

//有参构造， 线程执行函数   线程名称
Thread::Thread::Thread(std::function<void()> cb, std::string& name):m_cb(cb), m_name(name)
{
    if(name.empty())
    {
        m_name = "UNKNOW";
    }

    //m_thread: 线程句柄
    //attr:用于手动设置新建线程的属性，例如线程的调用策略、线程所能使用的栈内存的大小等,不设置传空
    //线程执行函数
    //线程执行函数参数
    int ret = pthread_create(&m_thread, nullptr, &Thread::run, this);
    if(ret)
    {
        FRAMEWORK_LOG_ERROR(g_logger) << "pthread_create thread fail, ret=" << ret << " name=" << name;
        throw std::logic_error("pthread_create error");
    }

    //防止构造函数返回时 线程还没有开始执行
    m_semaphore.wait();
}

Thread::~Thread()
{
    if(m_thread) 
    {
        pthread_detach(m_thread);
    }
}

//等待线程执行完成
void Thread::join()
{
    if(m_thread)
    {
        //pthread_join() 函数会一直阻塞调用它的线程，直至目标线程执行结束
        //pthread_join() 函数成功等到了目标线程执行结束（成功获取到目标线程的返回值），返回值为数字 0
        //参数:要阻塞的线程句柄，线程返回值
        int ret = pthread_join(m_thread, nullptr);
        if(ret)
        {
            FRAMEWORK_LOG_ERROR(g_logger) << "pthread_join thread fail, ret=" << ret << " name=" << m_name;
            throw std::logic_error("pthread_join error");
        }
        m_thread = 0;
    }
}

//线程执行函数
void* Thread::run(void* arg)
{
    Thread* thread = (Thread*)arg;
    t_thread = thread;
    t_thread_name = thread->m_name;
    thread->m_id = framework::GetThreadId();

    //pthread_setname_np用于设置指定线程的名称
    //pthread_setname_np参数:需要设置/获取 名称的线程    要设置/获取 名称的buffer(不能超过16个字节)
    //pthread_self() 获取调用线程的ID
    //top -H -p 进程号
    pthread_setname_np(pthread_self(), thread->m_name.substr(0,15).c_str());

    //防止有智能指针没有释放释放
    std::function<void()> cb;
    cb.swap(thread->m_cb);

    //唤醒构造函数，使其继续执行
    thread->m_semaphore.notify();
    cb();
    return 0;
}

}